﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Barockoco_Chat_Server
{
    class ChatServer
    {
        // This hash table stores users and connections (browsable by user)
        public static Hashtable htUsers = new Hashtable(30); // 30 users at one time limit

        // This hash table stores connections and users (browsable by connection)
        public static Hashtable htConnections = new Hashtable(30); // 30 users at one time limit

        // Will store the IP address passed to it
        private IPAddress ipAddress;
        private TcpClient tcpClient;

        // The event and its argument will notify the form when a user has connected, disconnected, send message, etc.
        public static event StatusChangedEventHandler StatusChanged;
        private static StatusChangedEventArgs e;

        // The constructor sets the IP address to the one retrieved by the instantiating object
        public ChatServer(IPAddress address)
        {
            ipAddress = address;
        }

        // The thread that will hold the connection listener
        private Thread thrListener;

        // The TCP object that listens for connections
        private TcpListener tlsClient;

        // Will tell the while loop to keep monitoring for connections
        // changed bool to public static bool for being accesible from other classes to check whether active or not
        public static bool ServRunning = false;

        // Add the user to the hash tables
        public static void AddUser(TcpClient tcpUser, string strUsername)
        {
            // First add the username and associated connection to both hash tables
            ChatServer.htUsers.Add(strUsername, tcpUser);
            ChatServer.htConnections.Add(tcpUser, strUsername);

            // Tell of the new connection to all other users and to the server form
            SendAdminMessage(htConnections[tcpUser] + " has joined us");
        }

        // Remove the user from the hash tables
        public static void RemoveUser(TcpClient tcpUser)
        {
            // If the user is there
            if (htConnections[tcpUser] != null)
            {
                // First show the information and tell the other users about the disconnection
                SendAdminMessage(htConnections[tcpUser] + " has left us");

                // Remove the user from the hash table
                ChatServer.htUsers.Remove(ChatServer.htConnections[tcpUser]);
                ChatServer.htConnections.Remove(tcpUser);
            }
        }

        // This is called when we want to raise the StatusChanged event
        public static void OnStatusChanged(StatusChangedEventArgs e)
        {
            StatusChangedEventHandler statusHandler = StatusChanged;
            if (statusHandler != null)
            {
                // Invoke the delegate
                statusHandler(null, e);
            }
        }

        // Send administrative messages
        public static void SendAdminMessage(string Message)
        {
            StreamWriter swSenderSender;

            // First of all, show in our application who says what
            e = new StatusChangedEventArgs("Administrator: " + Message);
            OnStatusChanged(e);

            // Create an array of TCP clients, the size of the number of users we have
            TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count];

            // Copy the TcpClient objects into the array
            ChatServer.htUsers.Values.CopyTo(tcpClients, 0);

            // Loop through the list of TCP clients
            for (int i = 0; i < tcpClients.Length; i++)
            {
                // Try sending a message to each
                try
                {
                    // If the message is blank or the connection is null, break out
                    if (Message.Trim() == "" || tcpClients[i] == null)
                    {
                        continue;
                    }
                    // Send the message to the current user in the loop
                    swSenderSender = new StreamWriter(tcpClients[i].GetStream());
                    swSenderSender.WriteLine("Administrator: " + Message);
                    swSenderSender.Flush();
                    swSenderSender = null;
                }
                catch // If there was a problem, the user is not there anymore, remove him
                {
                    RemoveUser(tcpClients[i]);
                }
            }
        }

        // Send messages from one user to all the others
        public static void SendMessage(string From, string Message)
        {
            StreamWriter swSenderSender;

            // First of all, show in our application who says what
            e = new StatusChangedEventArgs(From + " says: " + Message);
            OnStatusChanged(e);

            // Create an array of TCP clients, the size of the number of users we have
            TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count];

            // Copy the TcpClient objects into the array
            ChatServer.htUsers.Values.CopyTo(tcpClients, 0);

            // Loop through the list of TCP clients
            for (int i = 0; i < tcpClients.Length; i++)
            {
                // Try sending a message to each
                try
                {
                    // If the message is blank or the connection is null, break out
                    if (Message.Trim() == "" || tcpClients[i] == null)
                    {
                        continue;
                    }
                    // Send the message to the current user in the loop
                    swSenderSender = new StreamWriter(tcpClients[i].GetStream());
                    swSenderSender.WriteLine(From + " says: " + Message);
                    swSenderSender.Flush();
                    swSenderSender = null;
                }
                catch // If there was a problem, the user is not there anymore, remove him
                {
                    RemoveUser(tcpClients[i]);
                }
            }
        }

        public void StartListening()
        {
            // Get the IP of the first network device, however this can prove unreliable on certain configurations
            IPAddress ipaLocal = ipAddress;
            // Create the TCP listener object using the IP of the server and the specified port
            tlsClient = new TcpListener(ipaLocal,1986);
            // Start the TCP listener and listen for connections
            tlsClient.Start();
            // The while loop will check for true in this before checking for connections
            ServRunning = true;
            // Start the new tread that hosts the listener
            thrListener = new Thread(KeepListening);
            thrListener.Start();
        }
        
        public void StopListening()
        {
            // Stop the TCP listener and stop listening for connections
            tlsClient.Stop();
            // The while loop will check for true in this before checking for connections
            ServRunning = false;
            // Start the new tread that hosts the listener
            thrListener.Abort();
        }
        
        private void KeepListening()
        {
            // While the server is running
            while (ServRunning == true)
            {
                // Accept a pending connection
                tcpClient = tlsClient.AcceptTcpClient();
                // Create a new instance of Connection
                Connection newConnection = new Connection(tcpClient);
            }
        }
    }
    
    // This delegate is needed to specify the parameters we're passing with our event
    public delegate void StatusChangedEventHandler(object sender, StatusChangedEventArgs e);
    
    // Holds the arguments for the StatusChanged event
    public class StatusChangedEventArgs : EventArgs
    {
        // The argument we're interested in is a message describing the event
        private string EventMsg;

        // Property for retrieving and setting the event message
        public string EventMessage
        {
            get
            {
                return EventMsg;
            }
            set
            {
                EventMsg = value;
            }
        }

        // Constructor for setting the event message
        public StatusChangedEventArgs(string strEventMsg)
        {
            EventMsg = strEventMsg;
        }
    }

    // This class handels connections; there will be as many instances of it as there will be connected users

    class Connection/*: IDisposable*/
    {
        TcpClient tcpClient;
        // The thread that will send information to the client
        private Thread thrSender;
        private StreamReader srReceiver;
        private StreamWriter swSender;
        private string currUser;
        private string strResponse;
        private bool iAmAdmin = false;
        private bool ireallyAmAdmin = false;

        ////vars for mouseclicks
        //private const UInt32 MouseEventLeftDown = 0x0002;
        //private const UInt32 MouseEventLeftUp = 0x0004;
        ////user32 API import
        //[DllImport("user32", EntryPoint = "mouse_event")]
        //private static extern void mouse_event(UInt32 dwFlags, UInt32 dx, UInt32 dy, UInt32 dwData, IntPtr dwExtraInfo);

        //protected virtual void Dispose(bool disposing)
        //{
        //    if (disposing)
        //    {
        //        // dispose managed resources
        //        "was disposed werden soll".Close();
        //    }
        //    // free native resources
        //}

        //public void Dispose()
        //{
        //    Dispose(true);
        //    GC.SuppressFinalize(this);
        //}

        //this is for the process to send the keys to
        [DllImport("User32.dll", SetLastError = true)]
        public static extern int SetForegroundWindow(IntPtr hwnd);
        int ID;
        // Hier kann zum Beispiel "Notepad.exe" übergeben werden
        private void ProzessErstellen(string programmname, string workingDirectory)
        {
            Process P = new Process();
            P.StartInfo.FileName = programmname;
            P.StartInfo.WorkingDirectory = workingDirectory;
            P.Start();
            P.WaitForInputIdle();
            ID = P.Id;
        }
        
        private void TextSenden(string Text, string begindate, string enddate)
        {
            //process
            System.IntPtr MainHandle = Process.GetProcessById(ID).MainWindowHandle;

            //SetForegroundWindow(MainHandle);
            /*SendKeys.SendWait(Text);
            SendKeys.SendWait("{TAB}");
            Thread.Sleep(500);
            SendKeys.SendWait("RoterMilan5");
            SendKeys.SendWait("{ENTER}");*/
            //Run("C:\sdiapl\blcashtex_v3.exe")
            Thread.Sleep(5000);
            SendKeys.SendWait("SYSADM{TAB}");//Login
            Thread.Sleep(500);
            SendKeys.SendWait("RoterMilan5{ENTER}{F4}");//Login
            Thread.Sleep(500);
            SendKeys.SendWait("{F4}");//Kasse auswählen
            Thread.Sleep(1000);//wait for connect with timeout
            SendKeys.SendWait("%");//goto Kassenkontrolle
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{RIGHT}");//goto Kassenkontrolle
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{DOWN}");//goto Kassenkontrolle
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{DOWN}");//goto Kassenkontrolle
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{ENTER}");//goto Kassenkontrolle
            Thread.Sleep(1000);
            for(int i=1; i<=16;i++){
                SendKeys.SendWait("{TAB}");
                Thread.Sleep(250);//wait in case its slow - quick n dirty is nothing we need here
            }
            SendKeys.SendWait(begindate);
            Thread.Sleep(200);
            //SendKeys.SendWait("01.12.2010{TAB}31.12.2011{F8}");//entering date makes export impossible - simply press f8
            SendKeys.SendWait("{TAB}");
            SendKeys.SendWait(enddate);
            SendKeys.SendWait("{TAB}{F8}");
            Thread.Sleep(5000);//wait because sometines it really takes its time;
            SendKeys.SendWait("%L");//open Listview
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("%");
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("{right 3}");
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("{down}");
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("{ENTER}");
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("%");
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("{right 2}");
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("{down 3}");
            Thread.Sleep(1000);//wait
            SendKeys.SendWait("{ENTER}");
            // alt: SendKeys.SendWait("^D");
            Thread.Sleep(5000);
            SendKeys.SendWait("%({F4 2})");
            //;FileCopy("C:\sdiapl\Datev.asc", "C:\Daten\Buchhaltung\");
            //;Global $Counter = 0;
            //;Do;
                //;MsgBox("Notify","Im in Counter ","OK");
            //;	$Counter = $Counter + 1;
            //;Until FileExists("C:\Daten\Buchhaltung\Datev" & $counter & ".asc") = 0;
            //;MsgBox("Notify","Der Conter liegt bei " & $Counter,"OK");
            //;FileCopy("C:\sdiapl\Datev.asc", "C:\Daten\Buchhaltung\Datevsecond.asc");
            //;MsgBox("Notify","File Move Done","OK");
            Thread.Sleep(1000);
            SendKeys.SendWait("{F12}");
            SendKeys.SendWait("{ENTER}");
            Thread.Sleep(1000);
            //SendKeys.SendWait("{F12}");
            //Thread.Sleep(1000);
            SendKeys.SendWait("{F12}");
            Thread.Sleep(1000);
            SendKeys.SendWait("{ENTER}");
        }

        private void copydatei(string file, string endung, string from, string destination) 
        {
            //string source = from;
            //int counter = 1;
            //string tempdestination = destination;
            ////dateiname anhängen
            //source += file;
            //tempdestination += file;
            if (File.Exists(destination + file + "." + endung) == false)//wenn datei noch nicht existiert, kopiere dorthin
            {
                File.Copy(from, destination+"."+endung);
            }
            else
            {//sonst hänge 1 an bzw zähle hoch bis datei nicht existiert, dann kopiere dorhin
                string stringcounter = "";
                for (int i = 1; File.Exists(destination + file + stringcounter + "." + endung) == true; i++)
                {
                    stringcounter = i.ToString();
                }
                File.Copy(from, destination + file + stringcounter + "." + endung);
            }
        }

        private void changeEKP()
        {
            //process
            System.IntPtr MainHandle = Process.GetProcessById(ID).MainWindowHandle;
            // Variablen zur Umrechnung des Wertes
            double clipboardZahl = -1;

            //SetForegroundWindow(MainHandle);
            /*SendKeys.SendWait(Text);
            SendKeys.SendWait("{TAB}");
            Thread.Sleep(500);
            SendKeys.SendWait("RoterMilan5");
            SendKeys.SendWait("{ENTER}");*/
            //Run("C:\sdiapl\blcashtex_v3.exe")
            Thread.Sleep(5000);
            SendKeys.SendWait("SYSADM{TAB}");//Login
            Thread.Sleep(500);
            SendKeys.SendWait("RoterMilan5{ENTER}");//Login
            Thread.Sleep(500);
            SendKeys.SendWait("{F7}");//Lager auswählen
            Thread.Sleep(1000);//wait for connect with timeout
            SendKeys.SendWait("%");//goto Lagerverwaltung
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{RIGHT}");//goto Lagerverwaltung
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{DOWN}");//goto Lagerverwaltung
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{DOWN}");//goto Lagerverwaltung
            Thread.Sleep(250);//wait
            SendKeys.SendWait("{ENTER}");//goto Lagerverwaltung
            Thread.Sleep(1000);
            SendKeys.SendWait("{F8}");//search all
            Thread.Sleep(3000);
            for (int i = 1; i <= 19; i++) //goto EKP
            {
                SendKeys.SendWait("{TAB}");
                Thread.Sleep(250);//wait in case its slow - quick n dirty is nothing we need here
            }
            for (int i = 1; i <= 2265; i++)
            {
                //Clipboard.SetText(); ersetz durch:
                SendKeys.SendWait("+{F10}");//Kontextmenü öffnen
                Thread.Sleep(250);
                SendKeys.SendWait("K");//in clipboard kopieren
                Thread.Sleep(250);
                clipboardZahl = Double.Parse(Clipboard.GetText()); //Double Parsen
                //die Prozente draufrechnen
                clipboardZahl = clipboardZahl * 1.037485;
                clipboardZahl = Math.Round(clipboardZahl, 2); //korrekt auf 2 desimalstellen runden
                Clipboard.SetText(clipboardZahl.ToString()); //das ganze wieder ins clipboard kopieren
                SendKeys.SendWait("+{F10}");//Kontextmenü öffnen
                Thread.Sleep(250);
                SendKeys.SendWait("F");//aus clipboard einsetzen
                Thread.Sleep(250);
                SendKeys.SendWait("{F7}"); //Üernehmen
                Thread.Sleep(500);//MWST abfrage abwarten
                SendKeys.SendWait("{RIGHT}");//auf nein wechseln
                Thread.Sleep(500);
                SendKeys.SendWait("{ENTER}");//mit enter Nein bestätigen
                Thread.Sleep(500);
                SendKeys.SendWait("%N"); //Nächster Datensatz
                Thread.Sleep(250);
                for (int i2 = 1; i2 <= 20; i2++) //goto EKP
                {
                    SendKeys.SendWait("{TAB}");
                    Thread.Sleep(250);//wait in case its slow - quick n dirty is nothing we need here
                }
            }
        }

        // The constructor of the class takes in a TCP connection
        public Connection(TcpClient tcpCon)
        {
            tcpClient = tcpCon;
            // The thread that accepts the client and awaits messages
            thrSender = new Thread(AcceptClient);
            // The thread calls the AcceptClient() method
            thrSender.Start();
        }

        private void CloseConnection()
        {
            // Close the currently open objects
            tcpClient.Close();
            srReceiver.Close();
            swSender.Close();
        }

        // Occures when a new client is accepted
        private void AcceptClient()
        {
            srReceiver = new System.IO.StreamReader(tcpClient.GetStream());
            swSender = new System.IO.StreamWriter(tcpClient.GetStream());
            // Read the account information from the client
            currUser = srReceiver.ReadLine();
            // We got a response from the client
            if (currUser != "")
            {
                // Store the user name in the hash table
                if (ChatServer.htUsers.Contains(currUser) == true)
                {
                    // 0 means not connected
                    swSender.WriteLine("0|Dieser Benutzername ist bereits vergeben, wähle einen anderen.");
                    swSender.Flush();
                    CloseConnection();
                    return;
                }

                else if (currUser == "Buchhaltung") {
                    swSender.WriteLine("1");
                    swSender.Flush();
                    ChatServer.AddUser(tcpClient, currUser);
                    ChatServer.SendAdminMessage("Willkommen " + currUser + ", Bitte authentifieren Sie sich:");
                    iAmAdmin = true;
                }
                    
                else if (currUser == "Administrator")
                {
                    // 0 means not connected
                    swSender.WriteLine("0|Dieser Nutzername ist reserviert, wähle einen anderen.");
                    swSender.Flush();
                    CloseConnection();
                    return;
                }

                else
                {
                    // 1 means connected successfully
                    swSender.WriteLine("1");
                    swSender.Flush();
                    // Add the user to the hash tables and start listening for messages from him
                    ChatServer.AddUser(tcpClient, currUser);
                }
            }

            else
            {
                CloseConnection();
                return;
            }

            try
            {
                // Keep waiting for a message from the user
                while ((strResponse = srReceiver.ReadLine()) != "")
                {
                    // If it's invalid, remove the user
                    if (strResponse == null)
                    {
                        ChatServer.RemoveUser(tcpClient);
                    }
                    else if(strResponse == "KSchmidt" && iAmAdmin == true){
                        ChatServer.SendAdminMessage("Willkommen " + currUser + ". Du bist mit hohen Rechten eingeloggt!");
                        ireallyAmAdmin = true;
                    }
                    else if(strResponse.StartsWith("/admin") && ireallyAmAdmin==true && strResponse.Contains(";Lexware;")){
                        try
                        {
                            string begindate = "";
                            string enddate = "";
                            begindate = strResponse.Substring(strResponse.IndexOf(";begindate=")+11, 10);
                            enddate = strResponse.Substring(strResponse.IndexOf(";enddate=")+9, 10);
                            ChatServer.SendAdminMessage("Bitte warten Sie bis der Export fertiggestellt ist.");
                            ProzessErstellen("C:\\sdiapl\\blcashtex_v3.exe", "C:\\Daten\\Buchhaltung\\Lexware\\");
                            TextSenden("", begindate, enddate);//sendet Eingaben an die Anwendung
                            //copydatei("Lexware","txt", "C:\\sdiapl\\Lexware.txt", "C:\\Daten\\Buchhaltung\\Lexware\\");
                            ChatServer.SendAdminMessage("Der Lexware-Export ist nun unter X:\\Buchhaltung\\Lexware\\Lexware.txt verfügbar.");
                        }
                        catch (Exception e) 
                        {
                            MessageBox.Show(e.Message);
                            ChatServer.SendAdminMessage("Fehler: " + e.Message);
                            ChatServer.SendAdminMessage("Bitte wenden Sie sich an den Administrator unter 0170 - 855 31 15");
                        }
                    }

                    else if (strResponse.StartsWith("/admin") && ireallyAmAdmin == true && strResponse.Contains(";EKP;"))
                    {
                        try
                        {
                            ChatServer.SendAdminMessage("Ich hoffe, du hast ein Backup gemacht!!!! Das hier ist ungetestet. Bitte warten Sie bis die Änderungen an den Artikeln fertiggestellt sind.");
                            ProzessErstellen("C:\\sdiapl\\blcashtex_v3.exe", "C:\\Daten\\Buchhaltung\\Lexware\\");
                            changeEKP(); //sendet Eingaben an die Anwendung
                            ChatServer.SendAdminMessage("Die Änderungen sind nun fertiggestellt. Bitte prüfen, ggf. Backup wieder einspielen.");
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show(e.Message);
                            ChatServer.SendAdminMessage("Fehler: " + e.Message);
                            ChatServer.SendAdminMessage("Bitte wenden Sie sich an den Administrator unter 0170 - 855 31 15");
                        }
                    }

                    else
                    {
                        // Otherwise send the message to all the other users
                        ChatServer.SendMessage(currUser, strResponse);
                    }
                }
            }
            catch
            {
                // If anything went wrong with this user, disconnect him
                ChatServer.RemoveUser(tcpClient);
            }
        }
    }
    class commandprompt{

        //Der Splitter für den Anweisungsstring
        public static string[] splitter(string command) {
            // getting commands for cmd.exe seperated by ';'

            string[] Commandprompt = new string[4];

            // define which character is seperating fields
            char[] splitter = { ';' };
            //split it!
            Commandprompt = command.Split(splitter);
            return Commandprompt;
        }

        //Die Ausführung der Anweisung 
        public static void execute(string[] command)
        {
            // for each of it do new cmd.exe with the command, iterate it..
            for (int x = 1; x < command.Length; x++)
            {
                ChatServer.SendAdminMessage("Führe jetzt aus: " + command[x]);
                Process myProc = new Process();
                try
                {
                    //declare the process
                    myProc.StartInfo.WorkingDirectory = "C:\\windows\\system32\\";
                    myProc.StartInfo.FileName = "C:\\WINDOWS\\system32\\cmd.exe";
                    myProc.StartInfo.CreateNoWindow = true;
                    myProc.StartInfo.Arguments = "/C " + command[x];// /K is used for not being closed after execution of commands
                    // Start the process.
                    myProc.Start();
                    myProc.WaitForExit();
                    //myProc.CloseMainWindow();//brauchen wir nicht, da cmd.exe /C schließt sich selbst
                    ChatServer.SendAdminMessage("Execution of <<" + command[x] + ">> was successful! Moving on..");
                }
                catch (Exception e)
                {
                    ChatServer.SendAdminMessage("Ausführung von <<" + command[x] + ">> gab folgenden Fehler zurück: " + e.Message);
                    ChatServer.SendAdminMessage("Ausführung von <<" + command[x] + ">> war nicht erfolgreich, fahre fort..");
                }
            }
            ChatServer.SendAdminMessage("Habe die Anfrage abgearbeitet.");
        }
    }
}
